/*
 * Copyright (C) 2020 Agrui
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "gtest/gtest.h"
#include "MessageHandler.h"
#include <PolicyService.h>
#include <Action.h>
#include <Event.h>

#include <thread>
#include <unistd.h>
#include <mcheck.h>
#include "CheckPoint.h"

#define EVENT_TEST_EVENTA_TRUE_MSG   1
#define EVENT_TEST_EVENTB_TRUE_MSG   2
#define EVENT_TEST_EVENTC_TRUE_MSG   3
#define EVENT_TEST_EVENT_QUIT_MSG    100

static CheckPoint sCP;

CLASS(EventA) {
    INHERIT(Event);

    /*Constructor*/
    void (*__init__)(EventA* event);
    /*Destructor*/
    void (*__deinit__)(EventA* event);

};

static void A__init__(EventA* self) {
    Event* p = SUPER(self, Event);
    p->__init__(p, "testA", FALSE, FALSE);
    printf("EventA__init__ enter \n");
}

DEF_CLASS_METHOD(EventA, __init__, A__init__);

LOAD_CLASS(EventA) {
    __load_class_Event__(SUPER(self, Event));
    SET_MEMBER(EventA, __init__);
}


CLASS(EventB) {
    INHERIT(Event);
    /*Constructor*/
    void (*__init__)(EventB* event);
    /*Destructor*/
    void (*__deinit__)(EventB* event);
};

static void B__init__(EventB* self) {
    Event* p = SUPER(self, Event);
    p->__init__(p, "testB", FALSE, FALSE);
    printf("EventB__init__ enter \n");
}

DEF_CLASS_METHOD(EventB, __init__, B__init__);

LOAD_CLASS(EventB) {
    __load_class_Event__(SUPER(self, Event));
    SET_MEMBER(EventB, __init__);
}


CLASS(EventC) {
    INHERIT(Event);
    /*Constructor*/
    void (*__init__)(EventC* event);
    /*Destructor*/
    void (*__deinit__)(EventC* event);
};

static void C__init__(EventC* self) {
    Event* p = SUPER(self, Event);
    p->__init__(p, "testC", FALSE, TRUE);
    printf("EventC__init__ enter\n");
}

DEF_CLASS_METHOD(EventC, __init__, C__init__);

LOAD_CLASS(EventC) {
    __load_class_Event__(SUPER(self, Event));
    SET_MEMBER(EventC, __init__);
}


static void testHandlerMessage(Message* msg) {
    switch(msg->what) {
        case EVENT_TEST_EVENTA_TRUE_MSG: {
			PolicyService* service = (PolicyService*)msg->userObj;
            Event* event = getPolicyEvent(service, "testA");
            event->updateState(TRUE);
            sCP.add(0);
            break;
        }
        case EVENT_TEST_EVENTB_TRUE_MSG: {
			PolicyService* service = (PolicyService*)msg->userObj;
            Event* event = getPolicyEvent(service, "testB");
            event->updateState(TRUE);
            sCP.add(1);
            break;
        }
        case EVENT_TEST_EVENTC_TRUE_MSG: {
			PolicyService* service = (PolicyService*)msg->userObj;
            Event* event = getPolicyEvent(service, "testC");
            event->updateState(TRUE);
            sCP.add(2);
            break;
        }
        case EVENT_TEST_EVENT_QUIT_MSG: {
            sCP.add(3);
            quit(msg->target->mQueue);
            break;
        }
    }
}


void onLoadEventTest(ArrayList* eventList) {
    sCP.add(4);
    arrayListAdd(eventList, SUPER(newEventA(), Event));
    arrayListAdd(eventList, SUPER(newEventB(), Event));
    arrayListAdd(eventList, SUPER(newEventC(), Event));
}
void onLoadPolicyTest1(char* rule, int size) {
    sCP.add(5);
    OS_LOG("onLoadPolicyTest1 enter\n");
    strcpy(rule, "testA&&testB");
}

void onActionTest(void* eventData, void* userData) {
    sCP.add(6);
}

TEST(PolicyServiceTest, PolicyTestAction1) {
    setenv("MALLOC_TRACE", "memory.bin", 1);
    mtrace();
    int verifyData[] = {4,5,0,1,6,3};
    sCP.clean();
    sCP.setCheckPoint(&verifyData[0], ARRAY_SIZE(verifyData));
    prepare(TRUE);
    Handler* handler = newHandler(testHandlerMessage);
    PolicyService* service = newPolicyService(onLoadEventTest, onLoadPolicyTest1);
    setAction(service, newAction(NULL, handler, onActionTest));
    std::thread thread = std::thread([handler, service, this]() {
        for (;;) {
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            Message* msg = handlerObtainMessage(handler, EVENT_TEST_EVENTA_TRUE_MSG, NULL);
            msg->userObj = service;
            messageSendToTarget(msg);

            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            msg = handlerObtainMessage(handler, EVENT_TEST_EVENTB_TRUE_MSG, NULL);
            msg->userObj = service;
            messageSendToTarget(msg);

            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            msg = handlerObtainMessage(handler, EVENT_TEST_EVENT_QUIT_MSG, NULL);
            msg->userObj = service;
            messageSendToTarget(msg);
            return;
        }
    });
    loop();
    thread.join();
    deletePolicyService(service);
    deleteHandler(handler);
    ASSERT_TRUE(sCP.verify());
    sCP.clean();
    muntrace();
}


void onLoadPolicyTest2(char* rule, int size) {
    sCP.add(5);
    strcpy(rule, "testA&&!testB");
}

TEST(PolicyServiceTest, PolicyTestAction2) {
    int verifyData[] = {4,5,0,6,3};
    sCP.clean();
    sCP.setCheckPoint(&verifyData[0], ARRAY_SIZE(verifyData));

    prepare(TRUE);
    Handler* handler = newHandler(testHandlerMessage);
    PolicyService* service = newPolicyService(onLoadEventTest, onLoadPolicyTest2);
    setAction(service, newAction(NULL, handler, onActionTest));
    std::thread thread = std::thread([handler, service, this]() {
        for (;;) {
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            Message* msg = handlerObtainMessage(handler, EVENT_TEST_EVENTA_TRUE_MSG, NULL);
            msg->userObj = service;
            messageSendToTarget(msg);

            std::this_thread::sleep_for(std::chrono::milliseconds(50));
            msg = handlerObtainMessage(handler, EVENT_TEST_EVENT_QUIT_MSG, NULL);
            msg->userObj = service;
            messageSendToTarget(msg);
            return;
        }
    });
    loop();
    thread.join();
    deletePolicyService(service);
    deleteHandler(handler);
    ASSERT_TRUE(sCP.verify());
    sCP.clean();
}

void onLoadPolicyTest3(char* rule, int size) {
    sCP.add(5);
    strcpy(rule, "testA&&testB");
}

TEST(PolicyServiceTest, PolicyTestAction3) {
    int verifyData[] = {4,5,0,3};
    sCP.clean();
    sCP.setCheckPoint(&verifyData[0], ARRAY_SIZE(verifyData));
    prepare(TRUE);
    Handler* handler = newHandler(testHandlerMessage);
    PolicyService* service = newPolicyService(onLoadEventTest, onLoadPolicyTest3);
    setAction(service, newAction(NULL, handler, onActionTest));
    std::thread thread = std::thread([handler, service, this]() {
        for (;;) {
            std::this_thread::sleep_for(std::chrono::milliseconds(200));
            Message* msg1 = handlerObtainMessage(handler, EVENT_TEST_EVENTA_TRUE_MSG, NULL);
            msg1->userObj = service;
            messageSendToTarget(msg1);

            std::this_thread::sleep_for(std::chrono::milliseconds(200));
            Message* msg2 = handlerObtainMessage(handler, EVENT_TEST_EVENT_QUIT_MSG, NULL);
            msg2->userObj = service;
            messageSendToTarget(msg2);
            return;
        }
    });
    loop();
    thread.join();
    deletePolicyService(service);
    deleteHandler(handler);
    ASSERT_TRUE(sCP.verify());
    sCP.clean();
}

void onLoadPolicyTest4(char* rule, int size) {
    sCP.add(5);
    strcpy(rule, "testA&&testC");
}


TEST(PolicyServiceTest, PolicyTestAction4) {
    int verifyData[] = {4,5,2,0,3};
    sCP.clean();
    sCP.setCheckPoint(&verifyData[0], ARRAY_SIZE(verifyData));

    prepare(TRUE);
    Handler* handler = newHandler(testHandlerMessage);
    PolicyService* service = newPolicyService(onLoadEventTest, onLoadPolicyTest4);
    setAction(service, newAction(NULL, handler, onActionTest));
    std::thread thread = std::thread([handler, service, this]() {
        for (;;) {
            std::this_thread::sleep_for(std::chrono::milliseconds(200));
            Message* msg = handlerObtainMessage(handler, EVENT_TEST_EVENTC_TRUE_MSG, NULL);
            msg->userObj = service;
            messageSendToTarget(msg);

            std::this_thread::sleep_for(std::chrono::milliseconds(200));
            msg = handlerObtainMessage(handler, EVENT_TEST_EVENTA_TRUE_MSG, NULL);
            msg->userObj = service;
            messageSendToTarget(msg);

            std::this_thread::sleep_for(std::chrono::milliseconds(200));
            msg = handlerObtainMessage(handler, EVENT_TEST_EVENT_QUIT_MSG, NULL);
            msg->userObj = service;
            messageSendToTarget(msg);

            return;
        }
    });
    loop();
    thread.join();
    deletePolicyService(service);
    deleteHandler(handler);
    ASSERT_TRUE(sCP.verify());
    sCP.clean();
}

void onLoadPolicyTest5(char* rule, int size) {
    sCP.add(5);
    strcpy(rule, "testA||testB");
}


TEST(PolicyServiceTest, PolicyTestAction5) {
    int verifyData[] = {4,5,1,6,3};
    sCP.clean();
    sCP.setCheckPoint(&verifyData[0], ARRAY_SIZE(verifyData));

    prepare(TRUE);
    Handler* handler = newHandler(testHandlerMessage);
    PolicyService* service = newPolicyService(onLoadEventTest, onLoadPolicyTest5);
    setAction(service, newAction(NULL, handler, onActionTest));
    std::thread thread = std::thread([handler, service, this]() {
        for (;;) {
            std::this_thread::sleep_for(std::chrono::milliseconds(200));
            Message* msg = handlerObtainMessage(handler, EVENT_TEST_EVENTB_TRUE_MSG, NULL);
            msg->userObj = service;
            messageSendToTarget(msg);

            std::this_thread::sleep_for(std::chrono::milliseconds(200));
            msg = handlerObtainMessage(handler, EVENT_TEST_EVENT_QUIT_MSG, NULL);
            msg->userObj = service;
            messageSendToTarget(msg);

            return;
        }
    });
    loop();
    thread.join();
    deletePolicyService(service);
    deleteHandler(handler);
    ASSERT_TRUE(sCP.verify());
    sCP.clean();
}

